package com.github.kilianB.pcg.sync;

/**
 * A 64 bit State PcgRNG with 32 bit output. PCG-XSH-RR
 * <p>
 * 
 * The pcg family combines a linear congruential generators with a permutation
 * output function resulting in high quality pseudo random numbers.
 * <p>
 * 
 * The original concept was introduced by Melissa O’Neill please refer to
 * <a href="http://www.pcg-random.org/">pcg-random</a> for more information.
 * <p>
 * Opposed to RR this version performs a random shift rather than a random
 * rotation.
 * 
 * The RS instance permutates the output using the following function:
 * 
 * <pre>
 * {@code
 * ((state >>> 22) ^ state) >>> ((state >>> 61) + 22)
 * }
 * </pre>
 * 
 * This implementation is thread safe utilizing method level synchronization.
 * 
 * @author Kilian
 * @see <a href="http://www.pcg-random.org/">www.pcg-random.org</a>
 * @see PcgRR
 */
public class PcgRS extends RandomBaseSynchonized {

	private static final long serialVersionUID = 5926140731231683840L;

	/**
	 * Create a synchronized PcgRS instance seeded with with 2 longs generated by
	 * xorshift*. The values chosen are very likely not used as seeds in any other
	 * non argument constructor of any of the classes provided in this library.
	 */
	public PcgRS() {
		super();
	}

	/**
	 * Create a random number generator with the given seed and stream number. The
	 * seed defines the current state in which the rng is in and corresponds to
	 * seeds usually found in other RNG implementations. RNGs with different seeds
	 * are able to catch up after they exhaust their period and produce the same
	 * numbers.
	 * <p>
	 * 
	 * Different stream numbers alter the increment of the rng and ensure distinct
	 * state sequences
	 * <p>
	 * 
	 * Only generators with the same seed AND stream numbers will produce identical
	 * values
	 * <p>
	 * 
	 * @param seed         used to compute the starting state of the RNG
	 * @param streamNumber used to compute the increment for the lcg.
	 */
	public PcgRS(long seed, long streamNumber) {
		super(seed, streamNumber);
	}

	/**
	 * Copy constructor. <b>Has</b> to be implemented in all inheriting instances.
	 * This will be invoked through reflection! when calling {@link #split()} or
	 * {@link #splitDistinct()} If no special behavior is desired simply pass though
	 * the values.
	 * 
	 * This constructor should usually not be called manually as the seed and
	 * increment will just be set without performing any randomization.
	 * 
	 * @param seed         of the lcg. The value will be set and not altered.
	 * @param streamNumber used in the lcg as increment constant. has to be odd
	 * @param dummy        unused. Resolve signature disambiguate
	 */
	@Deprecated
	public PcgRS(long seed, long streamNumber, boolean dummy) {
		super(seed, streamNumber, true);
	}

	// C variant
	@Override
	protected int getInt(long state) {
		// No rotation
		return (int) (((state >>> 22) ^ state) >>> ((state >>> 61) + 22));
	}

	// C++ variant. We can calculae the offset formula on the fly using the code
	// below. With this
	// we also don't need to generate an int first and extract the bits afterwards
	// but can directly create
	// the datatype needed. Just adjust the xTypeBits to the amount of bits needed
	// for the datatype. This approach
	// Should be looked into in a different branch.

	// In c++ bitcount_t is typedefed as unsigned byte
//
//	
//	int bits = 64; 		// State bit
//	int xTypeBits = 32; // Output bits
//	int spareBits = bits - xTypeBits;

	// TODO this can be improved and made more generic if we work with 2^x states
	// and output bits
	// spareBits - x >= 2^(x+1) -> return x complement of 2 will default to zero so
	// take the x and solve for 0
	// .-> spareBits-x = 2 ^(x+1) |
	// -> spareBits-x = 2 *(2^x)// int opbits = spareBits - 5 >= 64 ? 5
//			: spareBits - 4 >= 32 ? 4 : spareBits - 3 >= 16 ? 3 : spareBits - 2 >= 4 ? 2 : spareBits - 1 >= 1 ? 1 : 0;
//	int mask = (1 << opbits) - 1;
//	int maxRandShift = mask; // Todo obsolete
//	int topSpare = opbits;
//	int bottomSpare = spareBits - topSpare;
//	int xShift = topSpare + (xTypeBits + maxRandShift) / 2;

//	}
//
//	public static void main(String args[]) {
//		long state = Long.MAX_VALUE / 3;
//		System.out.println("Modified:" + PcgRS.getInt1(state) + " Original:" + PcgRS.getInt2(state));
//	}

}
